package com.css.exception;

import cn.hutool.core.util.StrUtil;

import com.css.constant.R;
import com.css.constant.Result;
import com.css.constant.ResultCode;
import com.css.constant.SystemCodeEnum;
import com.css.util.ServletUtil;
import com.fasterxml.jackson.annotation.JsonProperty;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.internal.engine.ConstraintViolationImpl;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.lang.reflect.Field;
import java.util.stream.Collectors;

/*import org.springframework.web.servlet.NoHandlerFoundException;*/

/**
 * @ClassName ExceptionHandlerAdvice
 * @Auther qiumengmeng
 * @Date 2024/8/7 上午11:22
 */
@RestControllerAdvice
@Slf4j
public class ExceptionHandlerAdvice {

    @ExceptionHandler(value = Exception.class)
    public Result<String> defaultException(Exception ex) {
        //TODO 默认异常需要处理
        log.error("默认异常需要处理", ex);
        if (ex instanceof ResultCode) {
            return R.err(((ResultCode) ex).getCode(), ((ResultCode) ex).getMsg());
        }
        return Result.error(SystemCodeEnum.SYSTEM_ERROR);
    }


    @ExceptionHandler(value = ServiceException.class)
    public Result<String> serviceException(ServiceException ex) {
        logWarn(ex.getMessage());
        return Result.err(ex.getCode(), ex.getMsg());
    }

    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public Result methodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        log.error("方法请求错误", e);
        return Result.noAuth();
    }

    @ExceptionHandler(value = ServletException.class)
    public Result servletException(ServletException e) {
        /*
          TODO 直接捕获NoHandlerFoundException异常gateway会报错，所以捕获ServletException
         */
        /*if (e instanceof NoHandlerFoundException) {
            log.error("请求路径未找到:", ((NoHandlerFoundException) e).getRequestURL());
            return Result.error(SystemCodeEnum.SYSTEM_NO_FOUND);
        }*/
        log.error("ServletException:", e);
        return Result.error(SystemCodeEnum.SYSTEM_ERROR);
    }

    @ExceptionHandler(value = BindException.class)
    public Result<String> validException(BindException e) {
        String msg = "参数非法";
        if (e.getGlobalError() != null) {
            msg = e.getGlobalError().getDefaultMessage();
        } else if (e.getFieldError() != null) {
            if (e.getFieldError().unwrap(ConstraintViolationImpl.class).getMessageTemplate()
                    .startsWith("{")) {
                msg = e.getFieldError().getField() + ":" + e.getFieldError().getDefaultMessage();
            } else {
                msg = e.getFieldError().getDefaultMessage();
            }
        }
        log.error("BindException:{}", msg);
        return Result.err(SystemCodeEnum.SYSTEM_NO_VALID.getCode(), msg);
    }

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Result argumentNotValidException(MethodArgumentNotValidException e) {
        //log.error("MethodArgumentNotValidException:", e);
        BindingResult bindingResult = e.getBindingResult();
        if (bindingResult.getGlobalError() != null) {
            return Result.error(SystemCodeEnum.SYSTEM_NO_VALID,
                    bindingResult.getGlobalError().getDefaultMessage());
        } else if (bindingResult.getFieldError() != null) {
            String fieldName = bindingResult.getFieldError().getField();
            try {
                Field        field      = bindingResult.getTarget().getClass()
                        .getDeclaredField(bindingResult.getFieldError().getField());
                JsonProperty annotation = field.getDeclaredAnnotation(JsonProperty.class);
                if (annotation != null && StringUtils.isNotBlank(annotation.value())) {
                    fieldName = annotation.value();
                }
            } catch (NoSuchFieldException noSuchFieldException) {
                log.error(noSuchFieldException.getMessage());
            }
//            return Result.error(SystemCodeEnum.SYSTEM_NO_VALID, fieldName + ":" + bindingResult.getFieldError().getDefaultMessage() );
            return Result.error(SystemCodeEnum.SYSTEM_NO_VALID,
                    bindingResult.getFieldError().getDefaultMessage());
        } else {
            return Result.error(SystemCodeEnum.SYSTEM_NO_VALID);
        }
    }

    @ExceptionHandler(value = FeignException.class)
    public Result feignException(FeignException ex) {
        log.error("feign异常：", ex);
        if (ex instanceof FeignServiceException) {
            return Result.error(ex.status(), ex.getMessage());
        }
        String message = ex.toString();
        if (StrUtil.isNotEmpty(message) && message.matches("[\\u4e00-\\u9fa5]+(!?|！?)")) {
            return Result.error(SystemCodeEnum.SYSTEM_SERVER_ERROR, message);
        } else {
            return Result.error(SystemCodeEnum.SYSTEM_SERVER_ERROR);
        }
    }

    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<String> handleMethodArgumentNotValidException(HttpServletRequest req,
                                                                HttpMessageNotReadableException e) throws MethodArgumentNotValidException {
        log.error("请求体错误", e);
        return Result.error(SystemCodeEnum.HTTP_MESSAGE_NOT_READABLE);
    }

    /**
     * 接口参数异常统一处理
     */
    @ExceptionHandler(
            {HttpMediaTypeNotSupportedException.class
//                    , HttpMessageNotReadableException.class
                    , MethodArgumentTypeMismatchException.class
                    , MissingServletRequestParameterException.class
                    , MissingServletRequestPartException.class
                    , MultipartException.class})
    //@ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<String> handleHttpMediaTypeNotSupportedException(Exception e,
            HttpServletRequest request) {
        String msg = "", errMsg = "";
        if (e instanceof HttpMediaTypeNotSupportedException) {
            msg = "不支持的Content-Type";
        } else if (e instanceof MethodArgumentTypeMismatchException) {
            msg = "参数类型非法";
            errMsg = msg + ":" + e.getCause().getMessage();
//        } else if (e instanceof HttpMessageNotReadableException) {
//            msg = "缺少必需的请求参数";
        } else if (e instanceof MissingServletRequestParameterException) {
            msg = errMsg =
                    "缺少参数:" + ((MissingServletRequestParameterException) e).getParameterName();
        } else if (e instanceof MissingServletRequestPartException) {
            msg = "缺少文件";
            errMsg = "缺少文件" + ":" + ((MissingServletRequestPartException) e).getRequestPartName();
        } else if (e instanceof MultipartException) {
            msg = errMsg = "数据传输错误";
        }
        logWarn(msg + e.getMessage());
        return Result.err(SystemCodeEnum.SYSTEM_NO_VALID.getCode(), msg);
    }

    /**
     * Validation 校验类类异常统一处理
     */
    @ExceptionHandler({ValidationException.class})
    public Result<String> handleValidationException(Exception e, HttpServletRequest request) {
        String msg = e.getMessage();
        if (e instanceof ConstraintViolationException) {
            msg = ((ConstraintViolationException) e).getConstraintViolations()
                    .stream().map(o -> "参数非法".equals(o.getMessage()) ?
                            ((PathImpl) o.getPropertyPath()).getLeafNode() + ":" + o.getMessage()
                            : o.getMessage()
                    ).collect(Collectors.toList()).toString();
        }
        logWarn(msg + e.getMessage());
        return Result.err(SystemCodeEnum.SYSTEM_NO_VALID.getCode(), msg);
    }



    private void logWarn(String e) {
        log.error("访问者地址::{},请求::{}:{},参数::{},异常信息::{}", ServletUtil.getClientIP(),
                ServletUtil.getMethod(), ServletUtil.getUri(), ServletUtil.getParameter(), e);
    }

    private void logErr(String msg, Exception e) {
        log.error("访问者地址::{},请求::{}:{},参数::{},异常信息::{}", ServletUtil.getClientIP(),
                ServletUtil.getMethod(), ServletUtil.getUri(), ServletUtil.getParameter(), msg, e);
    }
}
